package org.maven.ide.eclipse.wtp.ext;

import java.io.BufferedInputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.project.MavenProject;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
import org.eclipse.wst.common.componentcore.resources.IVirtualResource;
import org.maven.ide.eclipse.core.MavenLogger;
import org.maven.ide.eclipse.project.IMavenProjectFacade;
import org.maven.ide.eclipse.wtp.JEEPackaging;

/**
 * WarProjectConfiguratorDelegate
 * 
 * @author jer@printstacktrace.org
 */
class WarProjectConfiguratorDelegate extends
		AbstractProjectConfiguratorDelegate implements
		IProjectConfiguratorDelegate {

	protected void configure(IProject project, MavenProject mavenProject,
			IProgressMonitor monitor) throws CoreException {
	}

	public void setModuleDependencies(IProject project,
			MavenProject mavenProject, IProgressMonitor monitor)
			throws CoreException {
		overlayWarProjectDependencies(project, mavenProject, monitor);
		overlayWarArchiveDependencies(project, mavenProject, monitor);
	}

	protected List<IMavenProjectFacade> getWorkspaceDependencies(
			IProject project, MavenProject mavenProject) {
		Set<IProject> projects = new HashSet<IProject>();
		List<IMavenProjectFacade> dependencies = new ArrayList<IMavenProjectFacade>();
		
		Set<Artifact> artifacts = mavenProject.getArtifacts();
		for (Artifact artifact : artifacts) {
			IMavenProjectFacade dependency = projectManager.getMavenProject(
					artifact.getGroupId(), artifact.getArtifactId(), artifact
							.getVersion());
			if (Artifact.SCOPE_COMPILE.equals(artifact.getScope())
					&& dependency != null
					&& !dependency.getProject().equals(project)
					&& dependency.getFullPath(artifact.getFile()) != null
					&& projects.add(dependency.getProject())) {
				dependencies.add(dependency);
			}
		}
		return dependencies;
	}

	protected void overlayWarProjectDependencies(IProject project,
			MavenProject mavenProject, IProgressMonitor monitor)
			throws CoreException {
		IVirtualComponent component = ComponentCore.createComponent(project);
		Set<IResource> overlayFolders = new LinkedHashSet<IResource>();

		for (IMavenProjectFacade dependency : getWorkspaceDependencies(project,
				mavenProject)) {
			String depPackaging = dependency.getPackaging();

			if (JEEPackaging.WAR.equals(JEEPackaging.getValue(depPackaging))) {

				IVirtualComponent depComponent = ComponentCore
						.createComponent(dependency.getProject());
				if (depComponent != null) {
					depComponent.create(IVirtualResource.NONE, monitor);
					IVirtualFolder vf = depComponent.getRootFolder();
					IFolder workFolder = project.getFolder("target/war/work/");
					if (!workFolder.exists())
						createFolder(workFolder,monitor);
					IFolder desFolder = project.getFolder("target/war/work/"
							+ dependency.getProject().getName());
					if (!desFolder.exists() || !desFolder.isLinked()) {
						createFolder((IFolder) desFolder.getParent(), monitor);
						if (desFolder.exists() && !desFolder.isLinked()) {
							desFolder.delete(true, monitor);
						}
						IFolder srcFolder = dependency.getProject().getFolder(
								"src/main/webapp");
						
						desFolder.createLink(srcFolder.getLocation(),
								IResource.REPLACE, monitor);
					}
					component.getRootFolder().createLink(
							desFolder.getProjectRelativePath(),
							IVirtualResource.NONE, monitor);					
					overlayFolders.add(desFolder);
					

					desFolder = project.getFolder("target/war/work/"
							+ dependency.getProject().getName() + "-classes");
					if (!desFolder.exists() || !desFolder.isLinked()) {
						createFolder((IFolder) desFolder.getParent(), monitor);
						if (desFolder.exists() && !desFolder.isLinked()) {
							desFolder.delete(true, monitor);
						}
						IFolder srcFolder = dependency.getProject().getFolder(
								"target/classes");
						if(srcFolder.exists())
							desFolder.createLink(srcFolder.getLocation(),
								IResource.REPLACE, monitor);
					}
					component.getRootFolder().getFolder("WEB-INF/classes").createLink(
							desFolder.getProjectRelativePath(),
							IVirtualResource.NONE, monitor);					
					overlayFolders.add(desFolder);					
				}
			}
		}
		// delete old overlay link
		if (project.getFolder("target/war/work/").exists()) {
			IResource[] members = project.getFolder("target/war/work/").members();
			for (int i = 0; i < members.length; i++) {
				if (members[i].isLinked()) {
					if (!overlayFolders.contains(members[i])) {
						members[i].delete(true, monitor);
					}
				}
			}
		}
	}

	protected void overlayWarArchiveDependencies(IProject project,
			MavenProject mavenProject, IProgressMonitor monitor)
			throws CoreException {
		Set<IResource> overlayFolders = new LinkedHashSet<IResource>();
		IVirtualComponent component = ComponentCore.createComponent(project);
		if (component == null)
			return;

		Set<Artifact> artifacts = mavenProject.getArtifacts();
		for (Artifact artifact : artifacts) {
			IMavenProjectFacade dependency = projectManager.getMavenProject(
					artifact.getGroupId(), artifact.getArtifactId(), artifact
							.getVersion());
			if (Artifact.SCOPE_COMPILE.equals(artifact.getScope())
					&& dependency == null
					&& JEEPackaging.WAR.equals(JEEPackaging.getValue(artifact
							.getType()))) {
				IFolder workFolder = project.getFolder("target/war/work/");
				if (!workFolder.exists())
					createFolder(workFolder,monitor);
				IFolder desFolder = project.getFolder("target/war/work/"
						+ artifact.getArtifactId() + "-" + artifact.getBaseVersion());
				if (desFolder.exists()
						&& ((desFolder.getLocalTimeStamp() < artifact.getFile()
								.lastModified()) || desFolder.isLinked())) {
					desFolder.delete(true, monitor);
				}
				if (!desFolder.exists()) {
					createFolder(desFolder, monitor);
					unwar(desFolder, artifact.getFile(), monitor);
				}
				component.getRootFolder().createLink(
						desFolder.getProjectRelativePath(),
						IVirtualResource.NONE, monitor);
				overlayFolders.add(desFolder);
			}
		}

		// delete old overlay folder
		if (project.getFolder("target/war/work/").exists()) {
			IResource[] members = project.getFolder("target/war/work/").members();
			for (int i = 0; i < members.length; i++) {
				if (!members[i].isLinked()) {
					if (!overlayFolders.contains(members[i])) {
						members[i].delete(true, monitor);
					}
				}
			}
		}
	}

	protected boolean createFolder(IFolder folder, IProgressMonitor monitor)
			throws CoreException {
		if (folder.exists())
			return false;
		if (!folder.getParent().exists())
			createFolder((IFolder) folder.getParent(), monitor);
		folder.create(true, true, monitor);
		return true;
	}

	protected boolean unwar(IFolder desFolder, File warFile,
			IProgressMonitor monitor) throws CoreException {
		try {
			ZipFile zipFile = new ZipFile(warFile);
			Enumeration<? extends ZipEntry> e = zipFile.entries();
			while (e.hasMoreElements()) {
				ZipEntry zipEntry = e.nextElement();
				String name = zipEntry.getName();
				if (!name.startsWith("META-INF") && !name.endsWith("web.xml")
						&& name.indexOf("lib/") == -1) {
					if (!zipEntry.isDirectory()) {
						IFile outFile = desFolder.getFile(name);
						if (!outFile.exists()) {
							monitor.subTask("Unzip "
									+ outFile.getFullPath().toOSString());
							try {
								createFolder((IFolder) outFile.getParent(),
										monitor);
								BufferedInputStream is = new BufferedInputStream(
										zipFile.getInputStream(zipEntry));
								outFile.create(is, IFile.FORCE, monitor);
								
								is.close();
							} finally {
								monitor.worked(1);
							}
						}
					}
				}
			}
			zipFile.close();
			desFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
		} catch (Exception ex) {
			MavenLogger.log("Error unzipping " + warFile, ex);
		}
		return true;
	}
}